package cn.jingyuan.owl.utils.core.text;

import cn.jingyuan.owl.utils.core.CharUtils;
import cn.jingyuan.owl.utils.core.NumberUtils;
import cn.jingyuan.owl.utils.core.StringUtils;
import cn.jingyuan.owl.utils.core.lang.PatternPool;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 字符串切分器
 */
public class StringSplitter {

    /**
     * 切分字符串路径，仅支持 Unix 分界符：/
     *
     * @param string 被切分的字符串
     *
     * @return 切分后的集合
     */
    public static List<String> splitPath(String string) {
        return splitPath(string, 0);
    }

    /**
     * 切分字符串路径，仅支持 Unix 分界符：/
     *
     * @param string 被切分的字符串
     *
     * @return 切分后的集合
     */
    public static String[] splitPathToArray(String string) {
        return toArray(splitPath(string));
    }

    /**
     * 切分字符串路径，仅支持 Unix 分界符：/
     *
     * @param string 被切分的字符串
     * @param limit 限制分片数
     *
     * @return 切分后的集合
     */
    public static List<String> splitPath(String string, int limit) {
        return split(string, StringUtils.C_SLASH, limit, true, true);
    }

    /**
     * 切分字符串路径，仅支持 Unix 分界符：/
     *
     * @param string 被切分的字符串
     * @param limit 限制分片数
     *
     * @return 切分后的集合
     */
    public static String[] splitPathToArray(String string, int limit) {
        return toArray(splitPath(string, limit));
    }

    /**
     * 切分字符串
     *
     * @param string 被切分的字符串
     * @param separator 分隔符字符
     * @param ignoreEmpty 是否忽略空串
     *
     * @return 切分后的集合
     */
    public static List<String> splitTrim(String string, char separator, boolean ignoreEmpty) {
        return split(string, separator, 0, true, ignoreEmpty);
    }

    /**
     * 切分字符串
     *
     * @param string 被切分的字符串
     * @param separator 分隔符字符
     * @param isTrim 是否去除切分字符串后每个元素两边的空格
     * @param ignoreEmpty 是否忽略空串
     *
     * @return 切分后的集合
     */
    public static List<String> split(String string, char separator, boolean isTrim, boolean ignoreEmpty) {
        return split(string, separator, 0, isTrim, ignoreEmpty);
    }

    /**
     * 切分字符串，大小写敏感，去除每个元素两边空白符
     *
     * @param string 被切分的字符串
     * @param separator 分隔符字符
     * @param limit 限制分片数，-1不限制
     * @param ignoreEmpty 是否忽略空串
     *
     * @return 切分后的集合
     */
    public static List<String> splitTrim(String string, char separator, int limit, boolean ignoreEmpty) {
        return split(string, separator, limit, true, ignoreEmpty, false);
    }

    /**
     * 切分字符串，大小写敏感
     *
     * @param string 被切分的字符串
     * @param separator 分隔符字符
     * @param limit 限制分片数，-1不限制
     * @param isTrim 是否去除切分字符串后每个元素两边的空格
     * @param ignoreEmpty 是否忽略空串
     *
     * @return 切分后的集合
     */
    public static List<String> split(String string, char separator, int limit, boolean isTrim, boolean ignoreEmpty) {
        return split(string, separator, limit, isTrim, ignoreEmpty, false);
    }

    /**
     * 切分字符串，忽略大小写
     *
     * @param string 被切分的字符串
     * @param separator 分隔符字符
     * @param limit 限制分片数，-1不限制
     * @param isTrim 是否去除切分字符串后每个元素两边的空格
     * @param ignoreEmpty 是否忽略空串
     *
     * @return 切分后的集合
     */
    public static List<String> splitIgnoreCase(String string, char separator, int limit, boolean isTrim, boolean ignoreEmpty) {
        return split(string, separator, limit, isTrim, ignoreEmpty, true);
    }

    /**
     * 切分字符串
     *
     * @param string 被切分的字符串
     * @param separator 分隔符字符
     * @param limit 限制分片数，-1不限制
     * @param isTrim 是否去除切分字符串后每个元素两边的空格
     * @param ignoreEmpty 是否忽略空串
     * @param ignoreCase 是否忽略大小写
     *
     * @return 切分后的集合
     */
    public static List<String> split(String string, char separator, int limit, boolean isTrim, boolean ignoreEmpty, boolean ignoreCase) {
        if (StringUtils.isEmpty(string)) {
            return new ArrayList<>(0);
        }
        if (limit == 1) {
            return addToList(new ArrayList<>(1), string, isTrim, ignoreEmpty);
        }

        final ArrayList<String> list = new ArrayList<>(limit > 0 ? limit : 16);
        int len = string.length();
        int start = 0;// 切分后每个部分的起始
        for (int i = 0; i < len; i++) {
            if (NumberUtils.equals(separator, string.charAt(i), ignoreCase)) {
                addToList(list, string.substring(start, i), isTrim, ignoreEmpty);
                start = i + 1;//i+1 同时将 start 与 i 保持一致

                // 检查是否超出范围（最大允许 limit-1 个，剩下一个留给末尾字符串）
                if (limit > 0 && list.size() > limit - 2) {
                    break;
                }
            }
        }
        return addToList(list, string.substring(start, len), isTrim, ignoreEmpty);// 收尾
    }

    /**
     * 切分字符串为字符串数组
     *
     * @param string 被切分的字符串
     * @param separator 分隔符字符
     * @param limit 限制分片数
     * @param isTrim 是否去除切分字符串后每个元素两边的空格
     * @param ignoreEmpty 是否忽略空串
     *
     * @return 切分后的集合
     */
    public static String[] splitToArray(String string, char separator, int limit, boolean isTrim, boolean ignoreEmpty) {
        return toArray(split(string, separator, limit, isTrim, ignoreEmpty));
    }

    //---------------------------------------------------------------------------------------------- Split by String

    /**
     * 切分字符串，不忽略大小写
     *
     * @param string 被切分的字符串
     * @param separator 分隔符字符串
     * @param isTrim 是否去除切分字符串后每个元素两边的空格
     * @param ignoreEmpty 是否忽略空串
     *
     * @return 切分后的集合
     */
    public static List<String> split(String string, String separator, boolean isTrim, boolean ignoreEmpty) {
        return split(string, separator, -1, isTrim, ignoreEmpty, false);
    }

    /**
     * 切分字符串，去除每个元素两边空格，忽略大小写
     *
     * @param string 被切分的字符串
     * @param separator 分隔符字符串
     * @param ignoreEmpty 是否忽略空串
     *
     * @return 切分后的集合
     */
    public static List<String> splitTrim(String string, String separator, boolean ignoreEmpty) {
        return split(string, separator, true, ignoreEmpty);
    }

    /**
     * 切分字符串，不忽略大小写
     *
     * @param string 被切分的字符串
     * @param separator 分隔符字符串
     * @param limit 限制分片数
     * @param isTrim 是否去除切分字符串后每个元素两边的空格
     * @param ignoreEmpty 是否忽略空串
     *
     * @return 切分后的集合
     */
    public static List<String> split(String string, String separator, int limit, boolean isTrim, boolean ignoreEmpty) {
        return split(string, separator, limit, isTrim, ignoreEmpty, false);
    }

    /**
     * 切分字符串，去除每个元素两边空格，忽略大小写
     *
     * @param string 被切分的字符串
     * @param separator 分隔符字符串
     * @param limit 限制分片数
     * @param ignoreEmpty 是否忽略空串
     *
     * @return 切分后的集合
     */
    public static List<String> splitTrim(String string, String separator, int limit, boolean ignoreEmpty) {
        return split(string, separator, limit, true, ignoreEmpty);
    }

    /**
     * 切分字符串，忽略大小写
     *
     * @param string 被切分的字符串
     * @param separator 分隔符字符串
     * @param limit 限制分片数
     * @param isTrim 是否去除切分字符串后每个元素两边的空格
     * @param ignoreEmpty 是否忽略空串
     *
     * @return 切分后的集合
     */
    public static List<String> splitIgnoreCase(String string, String separator, int limit, boolean isTrim, boolean ignoreEmpty) {
        return split(string, separator, limit, isTrim, ignoreEmpty, true);
    }

    /**
     * 切分字符串，去除每个元素两边空格，忽略大小写
     *
     * @param string 被切分的字符串
     * @param separator 分隔符字符串
     * @param limit 限制分片数
     * @param ignoreEmpty 是否忽略空串
     *
     * @return 切分后的集合
     */
    public static List<String> splitTrimIgnoreCase(String string, String separator, int limit, boolean ignoreEmpty) {
        return split(string, separator, limit, true, ignoreEmpty, true);
    }

    /**
     * 切分字符串
     *
     * @param string 被切分的字符串
     * @param separator 分隔符字符串
     * @param limit 限制分片数
     * @param isTrim 是否去除切分字符串后每个元素两边的空格
     * @param ignoreEmpty 是否忽略空串
     * @param ignoreCase 是否忽略大小写
     *
     * @return 切分后的集合
     */
    public static List<String> split(String string, String separator, int limit, boolean isTrim, boolean ignoreEmpty, boolean ignoreCase) {
        if (StringUtils.isEmpty(string)) {
            return new ArrayList<>(0);
        }
        if (limit == 1) {
            return addToList(new ArrayList<String>(1), string, isTrim, ignoreEmpty);
        }

        if (StringUtils.isEmpty(separator)) {// 分隔符为空时按照空白符切分
            return split(string, limit);
        } else if (separator.length() == 1) {// 分隔符只有一个字符长度时按照单分隔符切分
            return split(string, separator.charAt(0), limit, isTrim, ignoreEmpty, ignoreCase);
        }

        final ArrayList<String> list = new ArrayList<>();
        int len = string.length();
        int separatorLen = separator.length();
        int start = 0;
        int i = 0;
        while (i < len) {
            i = StringUtils.indexOf(string, separator, start, ignoreCase);
            if (i > -1) {
                addToList(list, string.substring(start, i), isTrim, ignoreEmpty);
                start = i + separatorLen;

                // 检查是否超出范围（最大允许 limit-1 个，剩下一个留给末尾字符串）
                if (limit > 0 && list.size() > limit - 2) {
                    break;
                }
            } else {
                break;
            }
        }
        return addToList(list, string.substring(start, len), isTrim, ignoreEmpty);
    }

    /**
     * 切分字符串为字符串数组
     *
     * @param string 被切分的字符串
     * @param separator 分隔符字符
     * @param limit 限制分片数
     * @param isTrim 是否去除切分字符串后每个元素两边的空格
     * @param ignoreEmpty 是否忽略空串
     *
     * @return 切分后的集合
     */
    public static String[] splitToArray(String string, String separator, int limit, boolean isTrim, boolean ignoreEmpty) {
        return toArray(split(string, separator, limit, isTrim, ignoreEmpty));
    }

    /**
     * 使用空白符切分字符串<br>
     * 切分后的字符串两边不包含空白符，空串或空白符串并不做为元素之一
     *
     * @param string 被切分的字符串
     * @param limit 限制分片数
     *
     * @return 切分后的集合
     */
    public static List<String> split(String string, int limit) {
        if (StringUtils.isEmpty(string)) {
            return new ArrayList<>(0);
        }
        if (limit == 1) {
            return addToList(new ArrayList<>(1), string, true, true);
        }

        final ArrayList<String> list = new ArrayList<>();
        int len = string.length();
        int start = 0;// 切分后每个部分的起始
        for (int i = 0; i < len; i++) {
            if (CharUtils.isBlankChar(string.charAt(i))) {
                addToList(list, string.substring(start, i), true, true);
                start = i + 1;//i+1 同时将 start 与 i 保持一致

                // 检查是否超出范围（最大允许 limit-1 个，剩下一个留给末尾字符串）
                if (limit > 0 && list.size() > limit - 2) {
                    break;
                }
            }
        }
        return addToList(list, string.substring(start, len), true, true);// 收尾
    }

    /**
     * 切分字符串为字符串数组
     *
     * @param string 被切分的字符串
     * @param limit 限制分片数
     *
     * @return 切分后的集合
     */
    public static String[] splitToArray(String string, int limit) {
        return toArray(split(string, limit));
    }

    /**
     * 通过正则切分字符串
     *
     * @param string 字符串
     * @param separatorRegex 分隔符正则
     * @param limit 限制分片数
     * @param isTrim 是否去除切分字符串后每个元素两边的空格
     * @param ignoreEmpty 是否忽略空串
     *
     * @return 切分后的集合
     */
    public static List<String> splitByRegex(String string, String separatorRegex, int limit, boolean isTrim, boolean ignoreEmpty) {
        final Pattern pattern = PatternPool.get(separatorRegex);
        return split(string, pattern, limit, isTrim, ignoreEmpty);
    }

    /**
     * 通过正则切分字符串
     *
     * @param string 字符串
     * @param separatorPattern 分隔符正则 {@link Pattern}
     * @param limit 限制分片数
     * @param isTrim 是否去除切分字符串后每个元素两边的空格
     * @param ignoreEmpty 是否忽略空串
     *
     * @return 切分后的集合
     */
    public static List<String> split(String string, Pattern separatorPattern, int limit, boolean isTrim, boolean ignoreEmpty) {
        if (StringUtils.isEmpty(string)) {
            return new ArrayList<>(0);
        }
        if (limit == 1) {
            return addToList(new ArrayList<>(1), string, isTrim, ignoreEmpty);
        }

        if (null == separatorPattern) {// 分隔符为空时按照空白符切分
            return split(string, limit);
        }

        final Matcher matcher = separatorPattern.matcher(string);
        final ArrayList<String> list = new ArrayList<>();
        int len = string.length();
        int start = 0;
        while (matcher.find()) {
            addToList(list, string.substring(start, matcher.start()), isTrim, ignoreEmpty);
            start = matcher.end();

            // 检查是否超出范围（最大允许 limit-1 个，剩下一个留给末尾字符串）
            if (limit > 0 && list.size() > limit - 2) {
                break;
            }
        }
        return addToList(list, string.substring(start, len), isTrim, ignoreEmpty);
    }

    /**
     * 通过正则切分字符串为字符串数组
     *
     * @param string 被切分的字符串
     * @param separatorPattern 分隔符正则 {@link Pattern}
     * @param limit 限制分片数
     * @param isTrim 是否去除切分字符串后每个元素两边的空格
     * @param ignoreEmpty 是否忽略空串
     *
     * @return 切分后的集合
     */
    public static String[] splitToArray(String string, Pattern separatorPattern, int limit, boolean isTrim, boolean ignoreEmpty) {
        return toArray(split(string, separatorPattern, limit, isTrim, ignoreEmpty));
    }

    /**
     * 根据给定长度，将给定字符串截取为多个部分
     *
     * @param string 字符串
     * @param len 每一个小节的长度
     *
     * @return 截取后的字符串数组
     */
    public static String[] splitByLength(String string, int len) {
        int partCount = string.length() / len;
        int lastPartCount = string.length() % len;
        int fixPart = 0;
        if (lastPartCount != 0) {
            fixPart = 1;
        }

        final String[] strs = new String[partCount + fixPart];
        for (int i = 0; i < partCount + fixPart; i++) {
            if (i == partCount + fixPart - 1 && lastPartCount != 0) {
                strs[i] = string.substring(i * len, i * len + lastPartCount);
            } else {
                strs[i] = string.substring(i * len, i * len + len);
            }
        }
        return strs;
    }

    /**
     * 将字符串加入 List 中
     *
     * @param list 列表
     * @param part 被加入的部分
     * @param isTrim 是否去除两端空白符
     * @param ignoreEmpty 是否略过空字符串（空字符串不做为一个元素）
     *
     * @return 列表
     */
    private static List<String> addToList(List<String> list, String part, boolean isTrim, boolean ignoreEmpty) {
        if (isTrim) {
            part = StringUtils.trim(part);
        }
        if (!ignoreEmpty || !part.isEmpty()) {
            list.add(part);
        }
        return list;
    }

    /**
     * List 转 Array
     *
     * @param list List
     *
     * @return Array
     */
    private static String[] toArray(List<String> list) {
        return list.toArray(new String[list.size()]);
    }

}
